home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1997 January
/
Macworld (1997-01).dmg
/
Games World
/
Shareware Games
/
Arcade
/
Mike's Breakout
/
MTSound.u
< prev
next >
Wrap
Text File
|
1996-10-11
|
20KB
|
582 lines
{send all comments/gripes/etc to ani@atlas.nmsu.edu}
{feel free to use this code for whatever you'd like. i'd be grateful i helped make someone's day just a bit brighter}
{note that this is the "Sound" unit combined with my own code. it probably should be split, the reason they are}
{combined once existed, but now it probably would be better to seperate them, but i'll let you do that}
{ This file has been processed by The THINK Pascal Source Converter, v1.1. }
{}
{Created: Tuesday, March 5, 1991 at 5:12 PM}
{ Sound.p}
{ Pascal Interface to the Macintosh Libraries}
{}
{ Copyright Apple Computer, Inc. 1986-1991}
{ All rights reserved}
{}
unit MTSound;
interface
uses
TypeConst;
const
swMode = -1; { Sound Driver modes }
ftMode = 1;
ffMode = 0;
synthCodeRsrc = 'snth'; { Resource types used by Sound Manager }
soundListRsrc = 'snd ';
twelfthRootTwo = 1.05946309434;
rate22khz = $56EE8BA3; { 22254.54545 in fixed-point }
rate11khz = $2B7745D1; { 11127.27273 in fixed-point }
{ synthesizer numbers for SndNewChannel }
squareWaveSynth = 1; {square wave synthesizer}
waveTableSynth = 3; {wave table synthesizer}
sampledSynth = 5; {sampled sound synthesizer}
{ old Sound Manager MACE synthesizer numbers }
MACE3snthID = 11;
MACE6snthID = 13;
{ command numbers for SndDoCommand and SndDoImmediate }
nullCmd = 0;
initCmd = 1;
freeCmd = 2;
quietCmd = 3;
flushCmd = 4;
reInitCmd = 5;
waitCmd = 10;
pauseCmd = 11;
resumeCmd = 12;
callBackCmd = 13;
syncCmd = 14;
emptyCmd = 15;
tickleCmd = 20;
requestNextCmd = 21;
howOftenCmd = 22;
wakeUpCmd = 23;
availableCmd = 24;
versionCmd = 25;
totalLoadCmd = 26;
loadCmd = 27;
scaleCmd = 30;
tempoCmd = 31;
freqDurationCmd = 40;
restCmd = 41;
freqCmd = 42;
ampCmd = 43;
timbreCmd = 44;
getAmpCmd = 45;
waveTableCmd = 60;
phaseCmd = 61;
soundCmd = 80;
bufferCmd = 81;
rateCmd = 82;
continueCmd = 83;
doubleBufferCmd = 84;
getRateCmd = 85;
sizeCmd = 90;
convertCmd = 91;
stdQLength = 128;
dataOffsetFlag = $8000;
waveInitChannelMask = $07;
waveInitChannel0 = $04;
waveInitChannel1 = $05;
waveInitChannel2 = $06;
waveInitChannel3 = $07;
{ channel initialization parameters }
initPanMask = $0003; { mask for right/left pan values }
initSRateMask = $0030; { mask for sample rate values }
initStereoMask = $00C0; { mask for mono/stereo values }
initCompMask = $FF00; { mask for compression IDs }
initChanLeft = $0002; { left stereo channel }
initChanRight = $0003; { right stereo channel }
initNoInterp = $0004; { no linear interpolation }
initNoDrop = $0008; { no drop-sample conversion }
initMono = $0080; { monophonic channel }
initStereo = $00C0; { stereo channel }
initMACE3 = $0300; { MACE 3:1 }
initMACE6 = $0400; { MACE 6:1 }
initChan0 = $0004; { channel 0 - wave table only }
initChan1 = $0005; { channel 1 - wave table only }
initChan2 = $0006; { channel 2 - wave table only }
initChan3 = $0007; { channel 3 - wave table only }
stdSH = $00; { Standard sound header encode value }
extSH = $FF; { Extended sound header encode value }
cmpSH = $FE; { Compressed sound header encode value }
notCompressed = 0; { compression ID's }
twoToOne = 1;
eightToThree = 2;
threeToOne = 3;
sixToOne = 4;
outsideCmpSH = 0; { MACE constants }
insideCmpSH = 1;
aceSuccess = 0;
aceMemFull = 1;
aceNilBlock = 2;
aceBadComp = 3;
aceBadEncode = 4;
aceBadDest = 5;
aceBadCmd = 6;
sixToOnePacketSize = 8;
threeToOnePacketSize = 16;
stateBlockSize = 64;
leftOverBlockSize = 32;
firstSoundFormat = $0001; { general sound format }
secondSoundFormat = $0002; { special sampled sound format (HyperCard) }
dbBufferReady = $00000001; { double buffer is filled }
dbLastBuffer = $00000004; { last double buffer to play }
sysBeepDisable = $0000; { SysBeep() enable flags }
sysBeepEnable = $0001;
unitTypeNoSelection = $FFFF; { unitTypes for AudioSelection.unitType }
unitTypeSeconds = $0000;
type
{ Structures for Sound Driver }
FreeWave = packed array[0..30000] of Byte;
FFSynthPtr = ^FFSynthRec;
FFSynthRec = record
mode: INTEGER;
count: Fixed;
waveBytes: FreeWave;
end;
Tone = record
count: INTEGER;
amplitude: INTEGER;
duration: INTEGER;
end;
Tones = array[0..5000] of Tone;
SWSynthPtr = ^SWSynthRec;
SWSynthRec = record
mode: INTEGER;
triplets: Tones;
end;
Wave = packed array[0..255] of Byte;
WavePtr = ^Wave;
FTSndRecPtr = ^FTSoundRec;
FTSoundRec = record
duration: INTEGER;
sound1Rate: Fixed;
sound1Phase: LONGINT;
sound2Rate: Fixed;
sound2Phase: LONGINT;
sound3Rate: Fixed;
sound3Phase: LONGINT;
sound4Rate: Fixed;
sound4Phase: LONGINT;
sound1Wave: WavePtr;
sound2Wave: WavePtr;
sound3Wave: WavePtr;
sound4Wave: WavePtr;
end;
FTSynthPtr = ^FTSynthRec;
FTSynthRec = record
mode: INTEGER;
sndRec: FTSndRecPtr;
end;
{ Structures for Sound Manager }
SndCommand = packed record
cmd: INTEGER;
param1: INTEGER;
param2: LONGINT;
end;
Time = LONGINT; { in half milliseconds }
const
StdQLengthMinusOne = stdQLength - 1;
type
SndChannelPtr = ^SndChannel;
SndChannel = packed record
nextChan: SndChannelPtr;
firstMod: Ptr; { reserved for the Sound Manager }
callBack: ProcPtr;
userInfo: LONGINT;
wait: Time; { The following is for internal Sound Manager use only.}
cmdInProgress: SndCommand;
flags: INTEGER;
qLength: INTEGER;
qHead: INTEGER; { next spot to read or -1 if empty }
qTail: INTEGER; { next spot to write = qHead if full }
queue: array[0..stdQLengthMinusOne] of SndCommand;
end;
const
stateBlockSizeMinusOne = stateBlockSize - 1;
type
{ MACE structures }
StateBlockPtr = ^StateBlock;
StateBlock = record
stateVar: array[0..stateBlockSizeMinusOne] of INTEGER;
end;
const
leftOverBlockSizeMinusOne = leftOverBlockSize - 1;
type
LeftOverBlockPtr = ^LeftOverBlock;
LeftOverBlock = record
count: LONGINT;
sampleArea: packed array[0..leftOverBlockSizeMinusOne] of Byte;
end;
ModRef = record
modNumber: INTEGER;
modInit: LONGINT;
end;
SndListPtr = ^SndListResource;
SndListResource = record
format: INTEGER;
numModifiers: INTEGER;
modifierPart: array[0..0] of ModRef; {This is a variable length array}
numCommands: INTEGER;
commandPart: array[0..0] of SndCommand; {This is a variable length array}
dataPart: packed array[0..0] of Byte; {This is a variable length array}
end;
SoundHeaderPtr = ^SoundHeader;
SoundHeader = packed record
samplePtr: Ptr; { if NIL then samples are in sampleArea }
length: LONGINT; { length of sound in bytes }
sampleRate: Fixed; { sample rate for this sound }
loopStart: LONGINT; { start of looping portion }
loopEnd: LONGINT; { end of looping portion }
encode: Byte; { header encoding }
baseFrequency: Byte; { baseFrequency value }
sampleArea: packed array[0..0] of Byte;
end;
CmpSoundHeaderPtr = ^CmpSoundHeader;
CmpSoundHeader = packed record
samplePtr: Ptr; { if nil then samples are in sample area }
numChannels: LONGINT; { number of channels i.e. mono = 1 }
sampleRate: Fixed; { sample rate in Apples Fixed point representation }
loopStart: LONGINT; { loopStart of sound before compression }
loopEnd: LONGINT; { loopEnd of sound before compression }
encode: Byte; { data structure used , stdSH, extSH, or cmpSH }
baseFrequency: Byte; { same meaning as regular SoundHeader }
numFrames: LONGINT; { length in frames ( packetFrames or sampleFrames ) }
AIFFSampleRate: extended; { IEEE sample rate }
markerChunk: Ptr; { sync track }
futureUse1: Ptr; { reserved by Apple }
futureUse2: Ptr; { reserved by Apple }
stateVars: StateBlockPtr; { pointer to State Block }
leftOverSamples: LeftOverBlockPtr; { used to save truncated samples between compression calls }
compressionID: INTEGER; { 0 means no compression, non zero means compressionID }
packetSize: INTEGER; { number of bits in compressed sample packet }
snthID: INTEGER; { resource ID of Sound Manager snth that contains NRT C/E }
sampleSize: INTEGER; { number of bits in non-compressed sample }
sampleArea: packed array[0..0] of Byte; { space for when samples follow directly }
end;
ExtSoundHeaderPtr = ^ExtSoundHeader;
ExtSoundHeader = packed record
samplePtr: Ptr; { if nil then samples are in sample area }
numChannels: LONGINT; { number of channels, ie mono = 1 }
sampleRate: Fixed; { sample rate in Apples Fixed point representation }
loopStart: LONGINT; { same meaning as regular SoundHeader }
loopEnd: LONGINT; { same meaning as regular SoundHeader }
encode: Byte; { data structure used , stdSH, extSH, or cmpSH }
baseFrequency: Byte; { same meaning as regular SoundHeader }
numFrames: LONGINT; { length in total number of frames }
AIFFSampleRate: extended; { IEEE sample rate }
markerChunk: Ptr; { sync track }
instrumentChunks: Ptr; { AIFF instrument chunks }
AESRecording: Ptr;
sampleSize: INTEGER; { number of bits in sample }
futureUse1: INTEGER; { reserved by Apple }
futureUse2: LONGINT; { reserved by Apple }
futureUse3: LONGINT; { reserved by Apple }
futureUse4: LONGINT; { reserved by Apple }
sampleArea: packed array[0..0] of Byte; { space for when samples follow directly }
end;
ConversionBlockPtr = ^ConversionBlock;
ConversionBlock = record
destination: INTEGER;
unused: INTEGER;
inputPtr: CmpSoundHeaderPtr;
outputPtr: CmpSoundHeaderPtr;
end;
SMStatusPtr = ^SMStatus;
SMStatus = packed record
smMaxCPULoad: INTEGER;
smNumChannels: INTEGER;
smCurCPULoad: INTEGER;
end;
SCStatusPtr = ^SCStatus;
SCStatus = record
scStartTime: Fixed;
scEndTime: Fixed;
scCurrentTime: Fixed;
scChannelBusy: BOOLEAN;
scChannelDisposed: BOOLEAN;
scChannelPaused: BOOLEAN;
scUnused: BOOLEAN;
scChannelAttributes: LONGINT;
scCPULoad: LONGINT;
end;
AudioSelectionPtr = ^AudioSelection;
AudioSelection = packed record
unitType: LONGINT;
selStart: Fixed;
selEnd: Fixed;
end;
SndDoubleBufferPtr = ^SndDoubleBuffer;
SndDoubleBuffer = packed record
dbNumFrames: LONGINT;
dbFlags: LONGINT;
dbUserInfo: array[0..1] of LONGINT;
dbSoundData: packed array[0..0] of Byte;
end;
SndDoubleBufferHeaderPtr = ^SndDoubleBufferHeader;
SndDoubleBufferHeader = packed record
dbhNumChannels: INTEGER;
dbhSampleSize: INTEGER;
dbhCompressionID: INTEGER;
dbhPacketSize: INTEGER;
dbhSampleRate: Fixed;
dbhBufferPtr: array[0..1] of SndDoubleBufferPtr;
dbhDoubleBack: ProcPtr;
end;
var {--------------------------NOTE I HAVE SOME VARIABLES HERE ----------------------}
CurrStatus: SCStatusPtr;
TheChan: SndChannelPtr;
CurrCmd: SndCommand;
Sounds: array[MINSND..MAXSND] of longint;
TempH: Handle;
CurrSound: integer;
Unimportant: OSErr;
Where: Ptr;
SndOffset: longint;
CoolnessCounter: integer;
function SndDoCommand (chan: SndChannelPtr; cmd: SndCommand; noWait: BOOLEAN): OSErr;
inline
$A803;
function SndDoImmediate (chan: SndChannelPtr; cmd: SndCommand): OSErr;
inline
$A804;
function SndNewChannel (var chan: SndChannelPtr; synth: INTEGER; init: LONGINT; userRoutine: ProcPtr): OSErr;
inline
$A807;
function SndDisposeChannel (chan: SndChannelPtr; quietNow: BOOLEAN): OSErr;
inline
$A801;
function SndPlay (chan: SndChannelPtr; sndHdl: Handle; async: BOOLEAN): OSErr;
inline
$A805;
function SndAddModifier (chan: SndChannelPtr; modifier: ProcPtr; id: INTEGER; init: LONGINT): OSErr;
inline
$A802;
function SndControl (id: INTEGER; var cmd: SndCommand): OSErr;
inline
$A806;
procedure SetSoundVol (level: INTEGER);
procedure GetSoundVol (var level: INTEGER);
procedure StartSound (synthRec: Ptr; numBytes: LONGINT; completionRtn: ProcPtr);
procedure StopSound;
function SoundDone: BOOLEAN;
function SndSoundManagerVersion: NumVersion;
inline
$203C, $000C, $0008, $A800;
function SndStartFilePlay (chan: SndChannelPtr; fRefNum: INTEGER; resNum: INTEGER; bufferSize: LONGINT; theBuffer: Ptr; theSelection: AudioSelectionPtr; theCompletion: ProcPtr; async: BOOLEAN): OSErr;
inline
$203C, $0D00, $0008, $A800;
function SndPauseFilePlay (chan: SndChannelPtr): OSErr;
inline
$203C, $0204, $0008, $A800;
function SndStopFilePlay (chan: SndChannelPtr; async: BOOLEAN): OSErr;
inline
$203C, $0308, $0008, $A800;
function SndChannelStatus (chan: SndChannelPtr; theLength: INTEGER; theStatus: SCStatusPtr): OSErr;
inline
$203C, $0010, $0008, $A800;
function SndManagerStatus (theLength: INTEGER; theStatus: SMStatusPtr): OSErr;
inline
$203C, $0014, $0008, $A800;
procedure SndGetSysBeepState (var sysBeepState: INTEGER);
inline
$203C, $0018, $0008, $A800;
function SndSetSysBeepState (sysBeepState: INTEGER): OSErr;
inline
$203C, $001C, $0008, $A800;
function SndPlayDoubleBuffer (chan: SndChannelPtr; theParams: SndDoubleBufferHeaderPtr): OSErr;
inline
$203C, $0020, $0008, $A800;
function MACEVersion: NumVersion;
inline
$203C, $0000, $0010, $A800;
procedure Comp3to1 (inBuffer: Ptr; outBuffer: Ptr; cnt: LONGINT; inState: Ptr; outState: Ptr; numChannels: LONGINT; whichChannel: LONGINT);
inline
$203C, $0004, $0010, $A800;
procedure Exp1to3 (inBuffer: Ptr; outBuffer: Ptr; cnt: LONGINT; inState: Ptr; outState: Ptr; numChannels: LONGINT; whichChannel: LONGINT);
inline
$203C, $0008, $0010, $A800;
procedure Comp6to1 (inBuffer: Ptr; outBuffer: Ptr; cnt: LONGINT; inState: Ptr; outState: Ptr; numChannels: LONGINT; whichChannel: LONGINT);
inline
$203C, $000C, $0010, $A800;
procedure Exp1to6 (inBuffer: Ptr; outBuffer: Ptr; cnt: LONGINT; inState: Ptr; outState: Ptr; numChannels: LONGINT; whichChannel: LONGINT);
inline
$203C, $0010, $0010, $A800;
function MTGetSoundHeaderOffset (sndHdl: Handle; var offset: longint): oserr;
procedure InitSound;
procedure KillSound;
procedure PlaySound (WhichSound: integer);
{ UsingSound }
implementation
procedure PlaySound;
begin
if CoolnessCounter > 0 then {we got the hiscore sound playing, just leave it}
exit(PlaySound);
{ Unimportant := SndChannelStatus(TheChan, sizeof(SCStatus), CurrStatus);}
{if (CurrStatus^.scChannelBusy) then}
{WhichSound := 0;}
{if WhichSound <> 135 then}
{begin}
with CurrCmd do
begin
cmd := bufferCmd;
param1 := 0;
param2 := Sounds[WhichSound];
end;
Unimportant := SndDoImmediate(TheChan, CurrCmd); {just stick it in a command and send it to the chan, man!}
CurrSound := WhichSound;
{end;}
end;
procedure KillSound;
begin
Unimportant := snddisposechannel(thechan, true);
disposeptr(ptr(thechan));
end; {just dispose it, Unimportant probably really IS important if you're doing something serious!}
procedure InitSound;
var
i: integer;
begin
SetSoundVol(1); {you can make it more/less quiet}
CurrSound := 0; {this was going to be for priority, but that didn't quite work}
Unimportant := sndnewchannel(TheChan, sampledSynth, 0, nil);
for i := MINSND to MAXSND do
begin
TempH := GetResource('snd ', i);
HLock(TempH);
Unimportant := MTGetSoundHeaderOffset(TempH, SndOffset);
Sounds[i] := longint(Ord4(TempH^) + SndOffset);
end; {just load each into an array to be used later}
PlaySound(128); {play intro sound}
end;
function MTGetSoundHeaderOffset (sndHdl: Handle; var offset: longint): oserr;
type {This function... i got it from Apple's inside mac book }
snd1header = record {on sound; }
format: integer; {basically it just finds the right place to }
numsynths: integer; {start playing a synth sound... in format 1 or format 2,}
end; {which incidentally is obsolete and was created only to }
snd1hdrptr = ^snd1header; {work with HyperCard... }
snd2header = record {This function does the same thing as the toolbox }
format: integer; {function GetSoundHeaderOffset, but only works with the }
refcount: integer; {3.0 Sound Manager, which i didn't have. }
end;
snd2hdrptr = ^snd2header;
intptr = ^integer;
sndcmdptr = ^sndcommand;
var
myptr: ptr;
myoffset: longint;
numsynths: integer;
numcmds: integer;
isdone: boolean;
myerr: oserr;
begin
myoffset := 0;
myptr := ptr(sndhdl^);
isdone := false;
myerr := noerr;
case snd1hdrptr(myptr)^.format of
firstsoundformat:
begin
numsynths := snd1hdrptr(myptr)^.numsynths;
myptr := ptr(ord4(myptr) + sizeof(snd1header));
myptr := ptr(ord4(myptr) + numsynths * (sizeof(integer) + sizeof(longint)));
end;
secondsoundformat:
begin
myptr := ptr(ord4(myptr) + sizeof(snd2header));
end;
otherwise
begin
myerr := badformat;
isdone := true;
end;
end;
numcmds := intptr(myptr)^;
myptr := ptr(ord4(myptr) + sizeof(integer));
while (numcmds >= 1) and (not isdone) do
begin
if (intptr(myptr)^ = buffercmd + dataoffsetflag) or (intptr(myptr)^ = soundcmd + dataoffsetflag) then
begin
myoffset := sndcmdptr(myptr)^.param2;
isdone := true;
end
else
begin
myptr := ptr(ord4(myptr) + sizeof(sndcommand));
numcmds := numcmds - 1;
end;
end;
offset := myoffset;
mtgetsoundheaderoffset := myerr;
end;
end.